﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace SurveySystem.Core
{
    public delegate void SurveyEventHandler(Survey survey, SurveySystemDataContext dc);

    public static class SurveysBLL
    {

        public static string Ping()
        {
            return "Pong!";
        }

        public static event SurveyEventHandler SurveyPublished = null;
        public static event SurveyEventHandler SurveyExpired = null;

        public static void PublishNewOpenSurvey(SurveyConfiguration surveyConfig, IEnumerable<string> choiceStrings)
        {
            SurveySystemDataContext dc = CommonUtils.GetDc();
            Survey survey = new Survey(surveyConfig, dc);
            survey.isOpen = true;

            FillChoices(choiceStrings, survey);
            dc.Surveys.InsertOnSubmit(survey);

            ExecPublish(dc, survey);
        }

        public static void PublishNewClosedSurvey(SurveyConfiguration surveyConfig, IEnumerable<string> choiceStrings,
            IEnumerable<Guid> usersToAccess, IEnumerable<int> groupsToAccess)
        {
            SurveySystemDataContext dc = CommonUtils.GetDc();
            Survey survey = new Survey(surveyConfig, dc);
            survey.isOpen = false;

            FillChoices(choiceStrings, survey);
            dc.Surveys.InsertOnSubmit(survey);

            if (usersToAccess != null)
            {
                IEnumerable<User> users =
                    from uid in usersToAccess
                    join u in dc.Users on uid equals u.id into usrs
                    from uu in usrs
                    select uu;

                foreach (User user in users)
                {
                    UserAccess userAccess = new UserAccess();
                    user.UserAccesses.Add(userAccess);
                    survey.UserAccesses.Add(userAccess);
                }
            }

            if (groupsToAccess != null)
            {
                IEnumerable<Group> groups =
                    from gid in groupsToAccess
                    join g in dc.Groups on gid equals g.id into grps
                    from gg in grps
                    select gg;

                foreach (Group group in groups)
                {
                    GroupAccess groupAccess = new GroupAccess() { };
                    group.GroupAccesses.Add(groupAccess);
                    survey.GroupAccesses.Add(groupAccess);
                }
            }

            ExecPublish(dc, survey);
        }

        private static void ExecPublish(SurveySystemDataContext dc, Survey survey)
        {
            CommonUtils.SubmitAndHandleConflicts(dc);
            try
            {
                if (SurveyPublished != null)
                    SurveyPublished(survey, dc);
            }
            catch { }
        }

        private static void FillChoices(IEnumerable<string> choiceStrings, Survey survey)
        {
            if (survey.surveyType != SurveyType.Free)
            {
                if (choiceStrings.Count() < 2)
                    throw new Exception("At least 2 choices needed for this surveytype!");

                char letter = 'A';
                foreach (string str in choiceStrings)
                {
                    Choice choice = new Choice() { letter = letter, text = str };
                    survey.Choices.Add(choice);
                    letter++;
                }
            }
        }
        public static Survey GetSurvey(int surveyId)
        {
            SurveySystemDataContext dc = CommonUtils.GetDc();
            Survey s = dc.Surveys.FirstOrDefault(su => su.id == surveyId);
            return s;
        }
        
        public static bool HasAccess(int surveyId, string userName)
        {
            return GetMySurveys(userName).Any(s => s.id == surveyId);
        }

        public static IEnumerable<Answer> GetMyAnswers(int surveyId, string userName)
        {
            User usr = UsersBLL.GetUser(userName);
            Survey s = GetSurvey(surveyId);

            IEnumerable<Answer> ans =  from a in s.Answers
                   where a.userId.CompareTo(usr.id) == 0
                   select a;

            return ans;
        }
        

        public static IEnumerable<Choice> GetChoices(int surveyId)
        {
           return GetSurvey(surveyId).Choices;
        
        }
        public static IEnumerable<Survey> GetMySurveys(string userName)
        {
            SurveySystemDataContext dc = CommonUtils.GetDc();

            User usr = UsersBLL.GetUser(userName);
            
            IEnumerable<Survey> res =
                from s in dc.Surveys
                where (s.UserAccesses.Any(u => u.userId == usr.id) || s.isOpen == true)
                select s;
            
            IEnumerable<Member> member =
                 from m in dc.Members
                 where (m.userId == usr.id)
                 select m;

            foreach (Member m in member)
            {
                IEnumerable<Survey> resG =
                    from s in dc.Surveys
                    where s.GroupAccesses.Any(g => g.groupId == m.groupId)
                    orderby s.expirationTime descending
                    select s;
                res = res.Union(resG);
            }
            return res;
                
        }
        public static IEnumerable<Survey> GetMyOpenSurveys(string userName)
        {
            SurveySystemDataContext dc = CommonUtils.GetDc();
            User usr = UsersBLL.GetUser(userName);
            
            IEnumerable<Survey> res =
                from s in dc.Surveys
                where (s.UserAccesses.Any(u => u.userId == usr.id) || s.isOpen) && s.isExpired == false
                //orderby s.expirationTime descending
                select s;

            IEnumerable<Member> member =
                 from m in dc.Members
                 where m.userId == usr.id && m.role != GroupMemberRole.Requesting
                 select m;


            foreach (Member m in member)
            {
                IEnumerable<Survey> resG =
                    from s in dc.Surveys
                    where s.GroupAccesses.Any(g => g.groupId == m.groupId) && s.isExpired == false
                    //orderby s.expirationTime descending
                    select s;
                res = res.Union(resG);
            }
            return res.OrderByDescending(s => s.publicationTime);
        }


        public static IEnumerable<Survey> GetMyOpenSurveysPaged(string userName,
            int maximumRows, int startRowIndex)
        {
            return GetMyOpenSurveys(userName).Skip(startRowIndex).Take(maximumRows);
        }

        public static int GetMyOpenSurveysCount(string userName)
        {
            return GetMyOpenSurveys(userName).Count();
        }

 
        public static IEnumerable<Survey> GetMyExpiredSurveys(string userName)
        {
            SurveySystemDataContext dc = CommonUtils.GetDc();
            User usr = UsersBLL.GetUser(userName);
           
            IEnumerable<Survey> res =
                from s in dc.Surveys
                where (s.UserAccesses.Any(u => u.userId == usr.id) || (s.isOpen)) && s.isExpired == true
                //orderby s.expirationTime descending
                select s;
             
            IEnumerable<Member> member =
                 from m in dc.Members
                 where m.userId == usr.id && m.role != GroupMemberRole.Requesting
                 select m;

            foreach (Member m in member)
            {
                IEnumerable<Survey> resG =
                    from s in dc.Surveys
                    where s.GroupAccesses.Any(g => g.groupId == m.groupId) && s.isExpired == true
                    //orderby s.expirationTime descending
                    select s;
                var x = resG.ToList();
                res = res.Union(resG);
            }
            return res.OrderByDescending( s => s.expirationTime) ;
        }

        public static IEnumerable<Survey> GetMyExpiredSurveysPaged(string userName,
            int maximumRows, int startRowIndex)
        {
            return GetMyExpiredSurveys(userName).Skip(startRowIndex).Take(maximumRows);
        }

        public static int GetMyExpiredSurveysCount(string userName)
        {
            return GetMyExpiredSurveys(userName).Count();
        }


        private static void DCUSR(string userName, out SurveySystemDataContext dc, out User user)
        {
            dc = CommonUtils.GetDc();
            user = dc.Users.FirstOrDefault(u => u.name == userName);
            if (user == null)
                throw new Exception("No such user!");
        }

        //public static void AddMultiAnwers(List<ChoiceAnswer> l)
        //{
        //    SurveySystemDataContext dc = CommonUtils.GetDc();
        //    Survey s = dc.Surveys.First(su => su.id == l[0].surveyId);
        //    if (s.isExpired) return;
        //    if (s.Answers.Any(a => a.userId == l[0].userId))
        //    {
        //        dc.Answers.DeleteAllOnSubmit(s.Answers.Where(a => a.userId == l[0].userId));
        //        dc.SubmitChanges();
        //    }
        //    foreach (ChoiceAnswer c in l)
        //    {
        //        dc.Answers.InsertOnSubmit(c);
        //    }
        //    dc.SubmitChanges();
        //}

        public static void AddMultiAnwers(int surveyId, string userName, IEnumerable<int> choiceIds)
        {
            SurveySystemDataContext dc;
            User user;
            Survey survey;
            Answer existing;
            IEnumerable<Answer> allExisting = ANSDATA(surveyId, userName, out dc, out user, out survey, out existing, "ChoiceAnswer");
            
            if (survey.isExpired == true)
            {
                Debug.Print("Lejárt szavazás");
                return;
            } 

            IEnumerable<Choice> choices = dc.Choices.Where(c => choiceIds.Contains(c.id));
            
            dc.Answers.DeleteAllOnSubmit(allExisting);
            foreach (Choice choice in choices)
            {
                ChoiceAnswer ca = new ChoiceAnswer() { Choice = choice };
                user.Answers.Add(ca);
                survey.Answers.Add(ca);
            }
            CommonUtils.SubmitAndHandleConflicts(dc);
        }

        public static void AddChoiceAnswer(int surveyId, string userName, int choiceId)
        {
            SurveySystemDataContext dc;
            User user;
            Survey survey;
            Answer existing;
            ANSDATA(surveyId, userName, out dc, out user, out survey, out existing, "ChoiceAnswer");

            if (survey.isExpired == true)
            {
                Debug.Print("Lejárt szavazás");
                return;
            } 
            Choice choice = dc.Choices.Where(c => c.id == choiceId).FirstOrDefault();
            
            if (choice == null)
                throw new Exception("No such choice!");

            

            if (existing != null)
                ((ChoiceAnswer)existing).Choice = choice;
            else
            {
                ChoiceAnswer ca = new ChoiceAnswer() { Choice = choice };
                user.Answers.Add(ca);
                survey.Answers.Add(ca);
            }
            
            CommonUtils.SubmitAndHandleConflicts(dc);
        }
        //public static void AddChoiceAnswer(ChoiceAnswer c)
        //{

        //}

        public static void AddFreeAnswer(int surveyId, string userName, string text)
        {
            SurveySystemDataContext dc;
            User user;
            Survey survey;
            Answer existing;
            ANSDATA(surveyId, userName, out dc, out user, out survey, out existing, "FreeAnswer");
            
            if (survey.isExpired == true)
            {
                Debug.Print("Lejárt szavazás");
                return;
            } 
            
            if (existing != null)
            {
                ((FreeAnswer)existing).text = text;
            }
            else
            {
                FreeAnswer a = new FreeAnswer()
                {
                    text = text
                };
                user.Answers.Add(a);
                survey.Answers.Add(a);
            }
            CommonUtils.SubmitAndHandleConflicts(dc);
            //FreeAnswer a;
            //a.
            //SurveySystemDataContext dc = CommonUtils.GetDc();
            //Survey s = dc.Surveys.First(su => su.id == c.surveyId);
            //if (s.isExpired) return;
            //if (s.Answers.Any(a => a.userId == c.userId))
            //{
            //    dc.Answers.DeleteAllOnSubmit(s.Answers.Where(a => a.userId == c.userId));
            //    dc.SubmitChanges();
            //}
            //dc.Answers.InsertOnSubmit(c);
            //dc.SubmitChanges();
        }

        private static IEnumerable<Answer> ANSDATA(int surveyId, string userName, out SurveySystemDataContext dc, out User user, out Survey survey, out Answer existing, string answerType)
        {
            DCUSR(userName, out dc, out user);

            survey = (from s in dc.Surveys
                      where s.id == surveyId
                      select s).FirstOrDefault();
            if (survey == null)
                throw new Exception("No such survey!");
            Guid userId = user.id;

            IEnumerable<Answer> allExisting =
                from a in dc.Answers
                where a.surveyId == surveyId && a.userId == userId && a.answerType == answerType
                select a;

            existing = allExisting.FirstOrDefault();
            return allExisting;
        }

        public static List<FreeAnswer> getTextAnswers(int surveyId)
        {
            List<FreeAnswer> l = new List<FreeAnswer>();
            Survey s = GetSurvey(surveyId);
            if (s.surveyType != SurveyType.Free)
            {
                return l;
            }
            foreach(FreeAnswer a in s.Answers)
                l.Add(a);
            return l;
        }

        public static List<AnswerSummary> getSummaryAnswers(int surveyId)
        {
            List<AnswerSummary> l = new List<AnswerSummary>();
            SurveySystemDataContext dc = CommonUtils.GetDc();
            Survey s = GetSurvey(surveyId);
            if (s.surveyType == SurveyType.Free) return new List<AnswerSummary>();
            foreach(Choice c in s.Choices)
            {
                AnswerSummary summary = new AnswerSummary();
                summary.AnswerCount = c.ChoiceAnswers.Count;
                summary.AnswerId = c.id;
                summary.Text = c.text;
                l.Add(summary);
            }
            return l;
        }

        internal static void GetExpiredSurveys()
        {
            SurveySystemDataContext dc = CommonUtils.GetDc();
            IEnumerable<Survey> s =
                from Survey surv in dc.Surveys
                where surv.isExpired == false && surv.expirationTime < DateTime.Now
                select surv;
            foreach(Survey su in s) 
            {
                su.isExpired = true;
                try
                {
                    SurveyExpired(su, dc);
                }
                catch { }
            }
            dc.SubmitChanges();
        }
    }
    public class AnswerSummary
    {
        private string text;
        private int answerCount;
        private int answerId;
        public string Text
        {
            get
            {
                return text;
            }
            set
            {
                text = value;
            }
        }
        public int AnswerCount
        {
            get
            {
                return answerCount;
            }
            set
            {
                answerCount = value;
            }
        }
        public int AnswerId
        {
            get
            {
                return answerId;
            }
            set
            {
                answerId = value;
            }
        }
    }
}
